/*
 * file:  program.h (contains declarations of Program, StmtList, Stmt, Expr)
 * Author: Jeremy Johnson 
 * Date: 2/05/07
 *
 * Description:
 */
#include <iostream>
#include <string>
#include <map>
#include <list>
#include <vector>

using namespace std;


// forward declarations 
// StmtList used by IfStmt and WhileStmt which are Stmt
// Proc which contains StmtList used in Expr, Stmt, StmtList 
class StmtList;
class Proc;

class Element;
class List;

class Expr
{
public:
	Expr() {};
	virtual ~Expr() {};  
	virtual Element eval(map<string,Element> NT, map<string,Proc*> FT)=0;  
	
	virtual void print(){};
	virtual bool isList(){return false;};

private:
};

/////////////////////////////
class Sequence
{
	public:
		Sequence(){};
		~Sequence(){};
	
		void insertListElement(Expr *listElement);
		void print();

	//private:
		std::vector<Expr *> listElements;//that would be either a List or a Number
		
		friend class List;
};

class List:public Expr
{
	public:
		List();
		List(Expr *l1, Expr *l2);
		~List();
		virtual Element eval(map<string,Element> NT, map<string,Proc*> FT);
		virtual void print();
		virtual bool isList(){return true;};

		void insertSequence(Sequence *seq);
		Sequence *getSequence(){return sequence;};
		int getListSize();
		
	//private:
		Sequence *sequence;
};

class Element
{
	public:
		Element(){};
		~Element(){};
		Element(int _val);
		Element(List *_lst);
		
		int value();
		List *getList(){return lst;};
		virtual void print();
		Element& operator=(const Element& elm);
		
		List *lst;
		int  val;
		bool isList;
};

class Concatenate:public Expr
{
	public:
		Concatenate(Expr * _l1 = NULL, Expr * _l2 = NULL){l1=_l1;l2=_l2;};
		virtual Element eval(map<string,Element> NT, map<string,Proc*> FT);
	
	private:
		Expr *l1,*l2;
};
class Cons:public Expr
{
	public:
		Cons(Expr * _exp = NULL, Expr * _list = NULL){expr = _exp;list = _list;};
		virtual Element eval(map<string,Element> NT, map<string,Proc*> FT);
	
	private:
		Expr *expr,*list;
};
/*
class Car:public Expr
{}
class Cdr:public Expr
{}
*/
class Nullp:public Expr
{
	public:
		Nullp(Expr * _exp){lst = _exp;};
		virtual Element eval(map<string,Element> NT, map<string,Proc*> FT);
	
	private:
		Expr *lst;
};
class Intp:public Expr
{
	public:
		Intp(Expr * _exp){exp = _exp;};
		virtual Element eval(map<string,Element> NT, map<string,Proc*> FT);
	
	private:
		Expr *exp;
};
class Listp:public Expr
{
	public:
		Listp(Expr * _exp){exp = _exp;};
		virtual Element eval(map<string,Element> NT, map<string,Proc*> FT);
	
	private:
		Expr *exp;
};

/////////////////////////////

class Number : public Expr
{
public:
	Number(int value = 0);
	virtual Element eval(map<string,Element> NT, map<string,Proc*> FT);
	void print();	
	int value();
private:
	int value_;
};


class Ident : public Expr
{
public:
	Ident(string name = "");
	virtual Element eval(map<string,Element> NT, map<string,Proc*> FT);
private:
	string name_;
};

class Times : public Expr
{
public:
	Times(Expr * op1 = NULL, Expr * op2 = NULL);
	virtual ~Times() {delete op1_; delete op2_;};
	virtual Element eval(map<string,Element> NT, map<string,Proc*> FT);
private:
	Expr* op1_;
	Expr* op2_;
};

class Plus : public Expr
{
public:
	Plus(Expr* op1 = NULL, Expr* op2 = NULL);
	virtual ~Plus() {delete op1_; delete op2_;};
	virtual Element eval(map<string,Element> NT, map<string,Proc*> FT);
private:
	Expr* op1_;
	Expr* op2_;
};

class Minus : public Expr
{
public:
	Minus(Expr* op1 = NULL, Expr* op2 = NULL);
	virtual ~Minus() {delete op1_; delete op2_;};
	virtual Element eval(map<string,Element> NT, map<string,Proc*> FT);
private:
	Expr* op1_;
	Expr* op2_;
};

class FunCall : public Expr
{
public:
	FunCall(string name, list<Expr*> *AL);
	virtual ~FunCall();
	virtual Element eval(map<string,Element> NT, map<string,Proc*> FT);
private:
	string name_;
	list<Expr*> *AL_;
};


class Stmt 
{
public:
	Stmt() {};
	virtual ~Stmt() {};  
	virtual void eval(map<string,Element> &NT, map<string,Proc*> &FT) const = 0;  

private:
};


class AssignStmt: public Stmt
{
public:
	AssignStmt(string name="", Expr *E=NULL);
	~AssignStmt() {delete E_;}; 
	void eval(map<string,Element> &NT, map<string,Proc*> &FT) const;
private:
	string name_;
	Expr* E_;
};

class DefineStmt : public Stmt
{ 
public:
	DefineStmt(string name="", Proc *P=NULL);
	virtual ~DefineStmt();  
	virtual void eval(map<string,Element> &NT, map<string,Proc*> &FT) const;  
private:
	string name_;
	Proc* P_;
};


class IfStmt: public Stmt
{
public:
	IfStmt(Expr *E,StmtList *S1, StmtList *S2);
	~IfStmt();
	void eval(map<string,Element> &NT, map<string,Proc*> &FT) const;
private:
	Expr* E_;
	StmtList *S1_;
	StmtList *S2_;
};

class WhileStmt: public Stmt
{
public:
	WhileStmt(Expr *E,StmtList *S);
        ~WhileStmt();
	void eval(map<string,Element> &NT, map<string,Proc*> &FT) const;
private:
	Expr* E_;
	StmtList *S_;
};


class StmtList 
{
public:
	StmtList() {};
	void eval(map<string,Element> &NT, map<string,Proc*> &FT);  
	void insert(Stmt *T);  

private:
	list<Stmt*> SL_;
};

class Proc
{
public:
	Proc(list<string> *PL, StmtList *SL);
	~Proc() {delete SL_; };  
	Element apply(map<string,Element> &NT, map<string,Proc*> &FT, list<Expr*> *EL);
private:
	StmtList *SL_;
	list<string> *PL_;
	int NumParam_;
};

class Program 
{
public:
	Program(StmtList *SL);
	~Program() {delete SL_; };  
	void dump();
	void eval();  

private:
	StmtList *SL_;
	map<string,Element> NameTable_;
	map<string,Proc*> FunctionTable_;
};
